/*
 * Highlighting of current row: does not work when L&F is set to Nimbus
 * Credits to Russ Hayward at 
 * http://stackoverflow.com/questions/5405550/highlight-current-row-in-jtextpane
 */

package newcrunch.gui;

import diff.BuiltInDiffProvider;
import diff.Difference;
import diff.HuntDiff;
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Rectangle;
import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.BadLocationException;
import jsyntaxpane.actions.ActionUtils;
import newcrunch.util.PropertyLoader;

/**
 * @author WL
 */
public class NewCrunchEditorPane extends JEditorPane 
{
        public static Color highlightRowColor = new Color(233, 239, 248);
        
        //diff sidebar properties
        private static int BAR_WIDTH = 9;
        private static Color colorAdded = new Color(150, 255, 150);
        private static Color colorChanged = new Color(160, 200, 255);
        private static Color colorRemoved = new Color(255, 160, 180);
        private static Color colorBorder = new Color(102, 102, 102);
        private BuiltInDiffProvider.Options options = new BuiltInDiffProvider.Options();
        private Difference[] cachedDiff;
        
        //unify all line separators to \n
        private static boolean keepLineSep = "\n".equals(System.getProperty("line.separator"));
        
        static
        {
            highlightRowColor = PropertyLoader.getColorProperty("caretrow");
            colorAdded = PropertyLoader.getColorProperty("colorAdded");
            colorChanged = PropertyLoader.getColorProperty("colorChanged");
            colorRemoved = PropertyLoader.getColorProperty("colorRemoved");
            colorBorder = PropertyLoader.getColorProperty("colorBorder");
        }
        
        public NewCrunchEditorPane()
        {
            setOpaque(false);
            SwingUtilities.invokeLater(new Runnable(){
                @Override
                public void run() {
                    NewCrunchEditorPane.this.getMargin().left = BAR_WIDTH;
                }
            });
        }
        
        public BuiltInDiffProvider.Options getDiffOptions()
        {
            return options;
        }
        
        public void regenerateDiffArr(String oldText, String newText)
        {
            if (oldText != null && newText != null)
            {
                cachedDiff = HuntDiff.diff(oldText.split("\n"), newText.split("\n"), options);
                revalidate();
            }
        }
        
        public void setCaretListener(final JLabel posLabel)
        {
            this.addCaretListener(new CaretListener(){

            @Override
            public void caretUpdate(CaretEvent e) {
                int pos = e.getDot();
                
                try {                    
                    int row = ActionUtils.getLineNumber(NewCrunchEditorPane.this, pos) + 1;
                    int col = ActionUtils.getColumnNumber(NewCrunchEditorPane.this, pos) + 1;
                    
                    posLabel.setText(String.valueOf(row) + ":" +String.valueOf(col) + " (" + String.valueOf(pos) + ")");
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
                
            });            
        }
        
        @Override
        public String getText()
        {
            return super.getText().replaceAll("\r\n", "\n");
        }
        
        @Override
        public void repaint(long tm, int x, int y, int width, int height) {
            // This forces repaints to repaint the entire TextPane.
            super.repaint(tm, 0, 0, getWidth(), getHeight());
        }
    
        /**
         * Some issues with Nimbus L&F.
         * Current line highlight not showing
         * under that L&F.
         * @param g 
         */
        @Override
        protected void paintComponent(Graphics g) 
        {
            g.setColor(getBackground());
            g.fillRect(0, 0, getWidth(), getHeight());
            try {
                Rectangle rect = modelToView(getCaretPosition());
                if (rect != null) {
                    g.setColor(highlightRowColor);
                    g.fillRect(this.getMargin().left, rect.y, getWidth(), rect.height);
                }
            } catch (BadLocationException e) {
                e.printStackTrace();
            }
            
            //painting the diff sidebar
            if (cachedDiff != null)
                paintDiff(g, cachedDiff);
            
            super.paintComponent(g);
        }
        
        private void paintDiff(Graphics g, Difference[] paintDiff)
        {
            Color oldColor = g.getColor();
            
            if (paintDiff == null || paintDiff.length == 0) {
                return;
            }

            FontMetrics fontMetrics = getFontMetrics(getFont());
            int fontHeight = fontMetrics.getHeight();
            int topMargin = getMargin().top;
            int[] yCoords = new int[3];

            for (int i = 0; i < paintDiff.length; i++)
            {
                Difference curr = paintDiff[i];
                switch (curr.getType())
                {
                    case Difference.ADD:
                    case Difference.CHANGE:
                        int startLine = curr.getSecondStart();
                        int endLine = curr.getSecondEnd();
                        int y = (startLine - 1) * fontHeight + topMargin;

                        //renders the coloured rectangle
                        g.setColor(getColor(curr));
                        g.fillRect(3, y, BAR_WIDTH - 3, (endLine - startLine + 1) * fontHeight);

                        //draws the vertical line
                        g.setColor(colorBorder);
                        int y1 = endLine * fontHeight + topMargin - 1;
                        g.drawLine(2, y, 2, y1);

                        //draws the horizontal line on top
                        if (curr.getSecondStart() == startLine)
                            g.drawLine(2, y, BAR_WIDTH - 1, y);

                        //draws the horizontal line below
                        g.drawLine(2, y1, BAR_WIDTH - 1, y1);                    
                        break;
                    case Difference.DELETE:
                        int line = (curr.getFirstStart() - 1) * fontHeight + topMargin;
                        yCoords[0] = line - 4;
                        yCoords[1] = line;
                        yCoords[2] = line + 4;
                        g.setColor(getColor(curr));
                        g.fillPolygon(new int[]{2, BAR_WIDTH, 2}, yCoords, 3);
                        g.setColor(colorBorder);
                        g.drawLine(2, yCoords[0], 2, yCoords[2] - 1);
                        break;
                }
            }
            
            g.setColor(oldColor);
        }
        
        private Color getColor(Difference ad) {
            if (ad.getType() == Difference.ADD) {
                return colorAdded;
            }
            if (ad.getType() == Difference.CHANGE) {
                return colorChanged;
            }
            return colorRemoved;
        }
}
